Skip to content

Conversation

@ryanbas21
Copy link
Collaborator

@ryanbas21 ryanbas21 commented Jan 22, 2026

JIRA Ticket

https://pingidentity.atlassian.net/browse/SDKS-4665

Description

Add support for well known endpoint

Summary by CodeRabbit

  • New Features

    • Added OpenID Connect well-known discovery support across clients with automatic realm inference and cached discovery retrieval.
    • Exposed shared well-known selector and discovery API for reuse.
  • Improvements

    • Validates well-known URLs (HTTPS required; HTTP allowed for localhost) and standardizes discovery error reporting.
    • Surface utilities and tests added for URL validation, realm inference, and discovery error handling.

✏️ Tip: You can customize this high-level summary in your review settings.

@changeset-bot
Copy link

changeset-bot bot commented Jan 22, 2026

🦋 Changeset detected

Latest commit: 95eee18

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 12 packages
Name Type
@forgerock/journey-client Minor
@forgerock/sdk-oidc Minor
@forgerock/sdk-utilities Minor
@forgerock/davinci-client Minor
@forgerock/oidc-client Minor
@forgerock/device-client Minor
@forgerock/protect Minor
@forgerock/sdk-types Minor
@forgerock/iframe-manager Minor
@forgerock/sdk-logger Minor
@forgerock/sdk-request-middleware Minor
@forgerock/storage Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai
Copy link

coderabbitai bot commented Jan 22, 2026

📝 Walkthrough

Walkthrough

Adds shared OIDC well-known discovery across packages: a new RTK Query wellknown API and error utilities, well-known URL/realm utilities, journey/davinci/oidc clients refactored to consume the shared module, tests added, and workspace/tsconfig dependency/reference updates.

Changes

Cohort / File(s) Summary
Journey Client - Well-known & utils
packages/journey-client/src/lib/wellknown.api.ts, packages/journey-client/src/lib/wellknown.utils.ts, packages/journey-client/src/lib/wellknown.utils.test.ts, packages/journey-client/src/types.ts
New re-exports of shared wellknown API; added hasWellknownConfig() type guard, inferRealmFromIssuer() realm extractor, re-exported isValidWellknownUrl and createWellknownError; tests for realm inference and config detection.
Journey Client - Config & Store
packages/journey-client/src/lib/config.types.ts, packages/journey-client/src/lib/client.store.ts, packages/journey-client/src/lib/client.store.utils.ts, packages/journey-client/src/lib/journey.slice.ts, packages/journey-client/src/lib/journey.api.ts
Introduced Async/Internal config types and JourneyConfigInput union; deferred config resolution to fetch well-known when present; normalizeConfig() signature changed; store creation API adjusted to include wellknown middleware and remove config extraArgument; API endpoints now read config from Redux state.
DaVinci Client - Well-known & types
packages/davinci-client/src/lib/wellknown.api.ts, packages/davinci-client/src/lib/wellknown.types.ts, packages/davinci-client/src/lib/config.types.ts, packages/davinci-client/src/lib/config.types.test-d.ts, packages/davinci-client/src/lib/client.store.ts
Replaced local RTK Query with re-exports from @forgerock/sdk-oidc; switched to WellKnownResponse from sdk-types; updated tests and client.store error handling to use createWellknownError.
OIDC Client - Well-known
packages/oidc-client/src/lib/wellknown.api.ts, packages/oidc-client/tsconfig.json
Re-exported shared wellknownApi and createWellknownSelector; added wellknownSelector() helper to read cached responses; removed several tsconfig project references.
SDK OIDC (sdk-effects/oidc)
packages/sdk-effects/oidc/src/lib/wellknown.api.ts, packages/sdk-effects/oidc/src/lib/wellknown.utils.ts, packages/sdk-effects/oidc/src/lib/wellknown.utils.test.ts, packages/sdk-effects/oidc/src/index.ts, packages/sdk-effects/oidc/package.json, packages/sdk-effects/oidc/tsconfig.json
New shared RTK Query wellknownApi with configuration endpoint and createWellknownSelector() factory; createWellknownError() normalizes FetchBaseQueryError/SerializedError shapes; tests added; added @reduxjs/toolkit dep and exported modules.
SDK Utilities - Well-known validation
packages/sdk-utilities/src/lib/wellknown/wellknown.utils.ts, packages/sdk-utilities/src/lib/wellknown/wellknown.utils.test.ts, packages/sdk-utilities/src/lib/wellknown/index.ts, packages/sdk-utilities/src/index.ts
Added isValidWellknownUrl() allowing HTTPS and HTTP for localhost; re-exported wellknown utilities and tests added.
Workspace & Tooling
package.json, nx.json, pnpm-workspace.yaml, packages/journey-client/package.json, packages/journey-client/tsconfig*.json, various e2e package.json/tsconfig files
NX and vitest/vite upgrades and reconfiguration; moved/updated tsconfig project references across packages and e2e apps; minor package.json reorganizations.
E2E & Misc
e2e/am-mock-api/package.json, e2e/am-mock-api/src/app/routes.resource.js, other e2e package json/tsconfig edits
Moved dev deps, updated express/body-parser versions, adjusted route path syntax and minor manifest reordering.
Tests / Small edits
packages/journey-client/src/lib/device/device-profile.test.ts, packages/sdk-utilities/src/lib/error/error.utils.test.ts
Test typing/import tweaks and minor comment removals.

Sequence Diagram(s)

sequenceDiagram
    participant Client as Journey Factory
    participant Store as Redux Store
    participant WKApi as wellknownApi (RTK Query)
    participant OIDC as OIDC Server
    participant Utils as Wellknown Utils

    Client->>Utils: isValidWellknownUrl(wellknownUrl)
    Utils-->>Client: boolean

    alt valid well-known
        Client->>WKApi: dispatch fetch configuration (endpoint)
        WKApi->>OIDC: GET /.well-known/openid-configuration
        OIDC-->>WKApi: WellKnownResponse
        WKApi-->>Client: cached WellKnownResponse
        Client->>Utils: inferRealmFromIssuer(response.issuer)
        Utils-->>Client: inferred realm?
        Client->>Store: dispatch setConfig(InternalJourneyClientConfig)
        Store-->>Client: config persisted in state
    else invalid / fetch error
        WKApi-->>Client: error
        Client->>Utils: createWellknownError(error)
        Utils-->>Client: GenericError
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • cerebrl
  • ancheetah

Poem

🐰 I hopped through .well-known, nose a-gleam,

Found realms in issuers and URLs that beam,
Shared APIs stitched configs with care,
Redux now holds the keys to share,
Hooray — discovery hops everywhere! 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Description check ❓ Inconclusive The PR description is sparse and generic, providing only a JIRA reference and minimal detail about the feature without explaining the implementation approach or impact. Expand the description to explain what well-known endpoint support entails, which configuration modes are now supported, and how the feature integrates with existing code.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat(journey-client): wellknown-endpoint-config-support' clearly summarizes the main change: adding well-known endpoint configuration support to the journey-client package.
Docstring Coverage ✅ Passed Docstring coverage is 94.44% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@nx-cloud
Copy link
Contributor

nx-cloud bot commented Jan 22, 2026

View your CI Pipeline Execution ↗ for commit 95eee18

Command Status Duration Result
nx run-many -t build --no-agents ✅ Succeeded <1s View ↗
nx affected -t build lint test e2e-ci ✅ Succeeded 1m 57s View ↗

☁️ Nx Cloud last updated this comment at 2026-01-27 22:17:52 UTC

@pkg-pr-new
Copy link

pkg-pr-new bot commented Jan 22, 2026

Open in StackBlitz

@forgerock/davinci-client

pnpm add https://pkg.pr.new/ForgeRock/ping-javascript-sdk/@forgerock/davinci-client@525

@forgerock/device-client

pnpm add https://pkg.pr.new/ForgeRock/ping-javascript-sdk/@forgerock/device-client@525

@forgerock/journey-client

pnpm add https://pkg.pr.new/ForgeRock/ping-javascript-sdk/@forgerock/journey-client@525

@forgerock/oidc-client

pnpm add https://pkg.pr.new/ForgeRock/ping-javascript-sdk/@forgerock/oidc-client@525

@forgerock/protect

pnpm add https://pkg.pr.new/ForgeRock/ping-javascript-sdk/@forgerock/protect@525

@forgerock/sdk-types

pnpm add https://pkg.pr.new/ForgeRock/ping-javascript-sdk/@forgerock/sdk-types@525

@forgerock/sdk-utilities

pnpm add https://pkg.pr.new/ForgeRock/ping-javascript-sdk/@forgerock/sdk-utilities@525

@forgerock/iframe-manager

pnpm add https://pkg.pr.new/ForgeRock/ping-javascript-sdk/@forgerock/iframe-manager@525

@forgerock/sdk-logger

pnpm add https://pkg.pr.new/ForgeRock/ping-javascript-sdk/@forgerock/sdk-logger@525

@forgerock/sdk-oidc

pnpm add https://pkg.pr.new/ForgeRock/ping-javascript-sdk/@forgerock/sdk-oidc@525

@forgerock/sdk-request-middleware

pnpm add https://pkg.pr.new/ForgeRock/ping-javascript-sdk/@forgerock/sdk-request-middleware@525

@forgerock/storage

pnpm add https://pkg.pr.new/ForgeRock/ping-javascript-sdk/@forgerock/storage@525

commit: 95eee18

@codecov-commenter
Copy link

codecov-commenter commented Jan 22, 2026

Codecov Report

❌ Patch coverage is 71.07692% with 94 lines in your changes missing coverage. Please review.
✅ Project coverage is 19.36%. Comparing base (b89ad58) to head (03b785b).
⚠️ Report is 33 commits behind head on main.

Files with missing lines Patch % Lines
packages/journey-client/src/lib/client.store.ts 57.72% 52 Missing ⚠️
packages/sdk-effects/oidc/src/lib/wellknown.api.ts 4.34% 22 Missing ⚠️
packages/journey-client/src/types.ts 0.00% 8 Missing ⚠️
packages/davinci-client/src/lib/client.store.ts 0.00% 7 Missing ⚠️
packages/sdk-effects/oidc/src/index.ts 0.00% 2 Missing ⚠️
packages/davinci-client/src/lib/wellknown.api.ts 0.00% 1 Missing ⚠️
packages/sdk-utilities/src/index.ts 0.00% 1 Missing ⚠️
packages/sdk-utilities/src/lib/wellknown/index.ts 50.00% 1 Missing ⚠️

❌ Your project status has failed because the head coverage (19.36%) is below the target coverage (40.00%). You can increase the head coverage or adjust the target coverage.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #525      +/-   ##
==========================================
+ Coverage   18.79%   19.36%   +0.57%     
==========================================
  Files         140      148       +8     
  Lines       27640    28005     +365     
  Branches      980     1028      +48     
==========================================
+ Hits         5195     5424     +229     
- Misses      22445    22581     +136     
Files with missing lines Coverage Δ
packages/davinci-client/src/lib/config.types.ts 100.00% <ø> (ø)
packages/davinci-client/src/lib/wellknown.types.ts 100.00% <ø> (ø)
...kages/journey-client/src/lib/client.store.utils.ts 100.00% <100.00%> (ø)
packages/journey-client/src/lib/journey.slice.ts 100.00% <100.00%> (ø)
packages/journey-client/src/lib/wellknown.api.ts 100.00% <100.00%> (ø)
packages/journey-client/src/lib/wellknown.utils.ts 100.00% <100.00%> (ø)
packages/oidc-client/src/lib/wellknown.api.ts 100.00% <100.00%> (ø)
...ckages/sdk-effects/oidc/src/lib/wellknown.utils.ts 100.00% <100.00%> (ø)
...sdk-utilities/src/lib/wellknown/wellknown.utils.ts 100.00% <100.00%> (ø)
packages/davinci-client/src/lib/wellknown.api.ts 50.00% <0.00%> (+43.75%) ⬆️
... and 7 more

... and 2 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@github-actions
Copy link
Contributor

github-actions bot commented Jan 22, 2026

Deployed bf383c5 to https://ForgeRock.github.io/ping-javascript-sdk/pr-525/bf383c5de59f21b2e7a6ffa8239192bf56991316 branch gh-pages in ForgeRock/ping-javascript-sdk

@github-actions
Copy link
Contributor

github-actions bot commented Jan 22, 2026

📦 Bundle Size Analysis

📦 Bundle Size Analysis

🚨 Significant Changes

🔺 @forgerock/sdk-utilities - 9.9 KB (+1.4 KB, +16.0%)
🔻 @forgerock/journey-client - 0.0 KB (-82.5 KB, -100.0%)
🔺 @forgerock/journey-client - 87.9 KB (+5.4 KB, +6.5%)
🔺 @forgerock/sdk-oidc - 7.2 KB (+4.6 KB, +182.2%)

📊 Minor Changes

📈 @forgerock/oidc-client - 23.8 KB (+0.3 KB)
📈 @forgerock/davinci-client - 40.0 KB (+0.2 KB)

➖ No Changes

@forgerock/device-client - 9.2 KB
@forgerock/protect - 150.1 KB
@forgerock/sdk-types - 8.0 KB
@forgerock/storage - 1.5 KB
@forgerock/sdk-logger - 1.6 KB
@forgerock/iframe-manager - 2.4 KB
@forgerock/sdk-request-middleware - 4.5 KB


13 packages analyzed • Baseline from latest main build

Legend

🆕 New package
🔺 Size increased
🔻 Size decreased
➖ No change

ℹ️ How bundle sizes are calculated
  • Current Size: Total gzipped size of all files in the package's dist directory
  • Baseline: Comparison against the latest build from the main branch
  • Files included: All build outputs except source maps and TypeScript build cache
  • Exclusions: .map, .tsbuildinfo, and .d.ts.map files

🔄 Updated automatically on each push to this PR

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/journey-client/src/lib/client.store.ts (1)

68-179: Apply request middleware to well-known discovery.
The discovery call is made via a temp store that doesn’t include requestMiddleware, so any custom headers/auth/fetch logic won’t apply to the well-known request. That can break environments that rely on middleware.

🔧 Proposed fix
 async function resolveAsyncConfig(
   config: JourneyConfigInput & { serverConfig: { wellknown: string } },
   log: ReturnType<typeof loggerFn>,
+  requestMiddleware?: RequestMiddleware[],
 ): Promise<InternalJourneyClientConfig> {
@@
-  const tempStore = createJourneyStore({ config: tempConfig, logger: log });
+  const tempStore = createJourneyStore({ config: tempConfig, logger: log, requestMiddleware });
@@
-    resolvedConfig = await resolveAsyncConfig(config, log);
+    resolvedConfig = await resolveAsyncConfig(config, log, requestMiddleware);
🤖 Fix all issues with AI agents
In `@packages/sdk-effects/oidc/package.json`:
- Line 31: Replace the explicit version for the dependency key
"@reduxjs/toolkit" in package.json (currently "^2.8.0") with the monorepo
catalog spec so it follows the workspace pattern — set the version to
"catalog:^2.8.2" (i.e., change the value for "@reduxjs/toolkit" to
"catalog:^2.8.2") so the package uses the single source of truth defined in
pnpm-workspace.yaml.
🧹 Nitpick comments (4)
packages/sdk-utilities/src/lib/wellknown/wellknown.utils.ts (1)

85-98: Consider adding IPv6 localhost support.

The function allows HTTP for localhost and 127.0.0.1, but not for ::1 (IPv6 localhost) or [::1]. This is a minor edge case but could cause unexpected behavior in IPv6-only development environments.

♻️ Optional: Add IPv6 localhost support
     // Allow HTTP only for localhost (development)
-    const isLocalhost = url.hostname === 'localhost' || url.hostname === '127.0.0.1';
+    const isLocalhost =
+      url.hostname === 'localhost' ||
+      url.hostname === '127.0.0.1' ||
+      url.hostname === '[::1]' ||
+      url.hostname === '::1';
     const isSecure = url.protocol === 'https:';
     const isHttpLocalhost = url.protocol === 'http:' && isLocalhost;
packages/oidc-client/src/lib/wellknown.api.ts (2)

33-38: Selector created on every call defeats memoization.

createSelector is invoked inside the function body, creating a new memoized selector on each call to wellknownSelector. This negates the memoization benefits since each call produces a fresh selector instance.

Consider creating the selector once per wellknownUrl or using createWellknownSelector from the re-exported utilities if it handles this pattern.

♻️ Option: Cache selectors by URL
+const selectorCache = new Map<string, ReturnType<typeof createSelector>>();
+
 export function wellknownSelector(wellknownUrl: string, state: RootState) {
-  const selector = createSelector(
-    wellknownApi.endpoints.configuration.select(wellknownUrl),
-    (result) => result?.data,
-  );
+  let selector = selectorCache.get(wellknownUrl);
+  if (!selector) {
+    selector = createSelector(
+      wellknownApi.endpoints.configuration.select(wellknownUrl),
+      (result) => result?.data,
+    );
+    selectorCache.set(wellknownUrl, selector);
+  }
   return selector(state);
 }

Alternatively, if createWellknownSelector (already exported) provides this functionality, consider using it directly instead of this wrapper.


18-21: Consider combining export and import statements.

The separate re-export (line 18) and import (line 21) from the same package can be consolidated.

♻️ Suggested consolidation
-export { wellknownApi, createWellknownSelector } from '@forgerock/sdk-oidc';
-
-// Import locally for use in selector below
-import { wellknownApi } from '@forgerock/sdk-oidc';
+import { wellknownApi, createWellknownSelector } from '@forgerock/sdk-oidc';
+
+export { wellknownApi, createWellknownSelector };
packages/journey-client/src/lib/wellknown.utils.test.ts (1)

64-69: Consider adding a comment explaining intentional type casts.

The type assertions (as AsyncJourneyClientConfig, as JourneyClientConfig) are used to bypass TypeScript's checks and test edge cases with invalid inputs. A brief comment would clarify this intent for future maintainers.

         const config: JourneyConfigInput = {
           serverConfig: {
             baseUrl: 'https://am.example.com/am/',
             wellknown: '',
           },
-        } as AsyncJourneyClientConfig;
+        } as AsyncJourneyClientConfig; // Intentionally cast to test runtime behavior with empty wellknown

Copy link
Contributor

@nx-cloud nx-cloud bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important

At least one additional CI pipeline execution has run since the conclusion below was written and it may no longer be applicable.

Nx Cloud is proposing a fix for your failed CI:

These changes fix the e2e test failure by replacing RTK Query with native fetch API in the wellknown configuration resolution. The original implementation created a temporary Redux store to fetch wellknown config, but RTK Query APIs are singletons with global middleware that caused conflicts when multiple journey client instances were created (e.g., after logout). By using fetch directly, we eliminate the store singleton issues while maintaining the wellknown endpoint discovery functionality.

Tip

We verified this fix by re-running @forgerock/journey-suites:e2e-ci--src/protect.test.ts.

Suggested Fix changes
diff --git a/packages/journey-client/src/lib/client.store.ts b/packages/journey-client/src/lib/client.store.ts
index 35c130bd..d7f3ac61 100644
--- a/packages/journey-client/src/lib/client.store.ts
+++ b/packages/journey-client/src/lib/client.store.ts
@@ -17,7 +17,6 @@ import { journeyApi } from './journey.api.js';
 import { setConfig } from './journey.slice.js';
 import { createStorage } from '@forgerock/storage';
 import { createJourneyObject } from './journey.utils.js';
-import { wellknownApi } from './wellknown.api.js';
 import {
   hasWellknownConfig,
   inferRealmFromIssuer,
@@ -80,19 +79,22 @@ async function resolveAsyncConfig(
     throw error;
   }
 
-  // Create a temporary store to fetch well-known (we need the RTK Query infrastructure)
-  const tempConfig: InternalJourneyClientConfig = {
-    serverConfig: { baseUrl: baseUrl || '', paths, timeout },
-    realmPath: config.realmPath,
-  };
-  const tempStore = createJourneyStore({ config: tempConfig, logger: log });
-
-  // Fetch the well-known configuration
-  const { data: wellknownResponse, error: fetchError } = await tempStore.dispatch(
-    wellknownApi.endpoints.configuration.initiate(wellknown),
-  );
+  // Fetch the well-known configuration directly using fetch API
+  // We avoid using RTK Query here to prevent store singleton issues
+  let wellknownResponse: any;
+  try {
+    const response = await fetch(wellknown, {
+      headers: {
+        Accept: 'application/json',
+      },
+    });
+
+    if (!response.ok) {
+      throw new Error(`HTTP ${response.status}: ${response.statusText}`);
+    }
 
-  if (fetchError || !wellknownResponse) {
+    wellknownResponse = await response.json();
+  } catch (fetchError: any) {
     const genericError = createWellknownError(fetchError);
     log.error(`${genericError.error}: ${genericError.message}`);
     throw new Error(genericError.message);

Apply fix via Nx Cloud  Reject fix via Nx Cloud


Or Apply changes locally with:

npx nx-cloud apply-locally o3Fs-SUDD

Apply fix locally with your editor ↗   View interactive diff ↗


🎓 Learn more about Self-Healing CI on nx.dev

Comment on lines -12 to +19
"body-parser": "^2.2.0",
"body-parser": "^2.2.2",
"cookie-parser": "^1.4.7",
"cors": "^2.8.5",
"express": "^4.21.2",
"express": "^5.2.1",
"superagent": "^10.2.3",
"uuid": "^13.0.0"
},
"devDependencies": {
"@types/express": "^4.17.17"
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is because i rebased my open deps pr because i was having issues with commands that were fixed via upgrading

export default function (app) {
// Passthrough route that enforces authentication
app.all('/resource/*', async (req, res, next) => {
app.all('/resource/{*splat}', async (req, res, next) => {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed when i rebased open-deps (part of this express upgrade)

"lint-staged": "^15.0.0",
"madge": "8.0.0",
"nx": "21.2.3",
"nx": "22.3.3",
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

all of this is because of dependency upgrades

return async () => {
await serverInfo.set(serverSlice);
const setResult = await serverInfo.set(serverSlice);
if (isGenericError(setResult)) {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

helps prevent type casting.

end_session_endpoint: 'https://example.com/logout',
pushed_authorization_request_endpoint: '',
check_session_iframe: '',
introspection_endpoint: '',
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per the https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata, only a few properties are REQUIRED - most are optional.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/journey-client/src/lib/journey.api.ts (1)

118-186: Guard against missing journey slice to avoid a TypeError.

If the store is misconfigured, state.journey can be undefined and throw before the explicit error is raised.

🛡️ Suggested defensive guard
-        const state = api.getState() as JourneyRootState;
-        const config = state.journey.config;
+        const state = api.getState() as JourneyRootState;
+        const config = state?.journey?.config;
         if (!config?.serverConfig) {
           throw new Error('Server configuration is missing.');
         }
@@
-        const state = api.getState() as JourneyRootState;
-        const config = state.journey.config;
+        const state = api.getState() as JourneyRootState;
+        const config = state?.journey?.config;
         if (!config?.serverConfig) {
           throw new Error('Server configuration is missing.');
         }
@@
-        const state = api.getState() as JourneyRootState;
-        const config = state.journey.config;
+        const state = api.getState() as JourneyRootState;
+        const config = state?.journey?.config;
         if (!config?.serverConfig) {
           throw new Error('Server configuration is missing.');
         }
🤖 Fix all issues with AI agents
In `@e2e/am-mock-api/package.json`:
- Line 14: Update the devDependency entry for "@types/express" to a
v5-compatible range (e.g., "^5.0.0") so it matches the installed "express" v5.x
and avoids type mismatches; modify the "@types/express" version in package.json
(devDependencies) and then reinstall/update the lockfile (npm/yarn) to ensure
the new types are applied.

In `@package.json`:
- Around line 62-73: Run the NX migration to apply breaking-change updates by
executing npx nx migrate latest and npx nx migrate --run-migrations, then update
configuration related to the nx-release-publish target (verify its release
config structure matches NX 22 expectations), ensure any custom plugins use
createNodesV2 and that TypeScript settings explicitly set
useLegacyTypescriptPlugin if needed (defaults changed to false), and remove
references or reliance on removed items like NX_DISABLE_DB, experimental JS
executor inlining, and legacy options; finally run the workspace
lint/build/tests to confirm everything works.
- Around line 87-88: Upgrade to Vitest 4.0.9 may introduce breaking changes; run
the full test suite (unit, integration, and snapshot tests) and verify
mocking/reporters after bumping "@vitest/coverage-v8" and "@vitest/ui". If
failures occur, update test config and code references: replace any legacy
pool/worker options with modern names (e.g., maxThreads → maxWorkers), ensure
coverage settings use coverage.include and provider: 'v8' (remove
coverage.all/coverage.extensions), adapt any deprecated test API signatures and
mocking/snapshot usage, and regenerate snapshots where appropriate; confirm
reporter output still matches expected formats.

In `@packages/davinci-client/src/lib/config.types.test-d.ts`:
- Around line 94-122: Tests assert that introspection_endpoint and
revocation_endpoint are required, but the comment (and RFC 8414) says they're
optional; fix by making the test and comment consistent: update the comment near
WellKnownResponse to state that only issuer, authorization_endpoint,
token_endpoint, and userinfo_endpoint are required, then remove the expectTypeOf
assertions for introspection_endpoint and revocation_endpoint (the other
expectTypeOf calls for
issuer/authorization_endpoint/token_endpoint/userinfo_endpoint should remain),
or alternatively change the WellKnownResponse type to mark
introspection_endpoint and revocation_endpoint optional if you control that
type; reference symbols: WellKnownResponse and the expectTypeOf assertions in
the test.

In `@packages/journey-client/package.json`:
- Around line 33-34: Remove the build/test tools from the runtime dependencies:
delete the "vite" and "vitest-canvas-mock" entries from the dependencies block
so they exist only under devDependencies; locate the package.json dependencies
array (look for the "vite" and "vitest-canvas-mock" keys) and remove those keys,
leaving the current devDependencies entries intact.

In `@packages/journey-client/src/lib/wellknown.api.ts`:
- Line 14: Add createWellknownError to the barrel export so consumers can import
it alongside wellknownApi and createWellknownSelector; update the export
statement in wellknown.api.ts (currently exporting wellknownApi and
createWellknownSelector) to also export createWellknownError from
'@forgerock/sdk-oidc' to maintain API parity with davinci-client and support
usage in client.store.ts and wellknown.utils.ts.

In `@packages/journey-client/src/lib/wellknown.utils.ts`:
- Around line 38-46: The type guard hasWellknownConfig currently only checks for
serverConfig.wellknown and can incorrectly narrow JourneyConfigInput to
AsyncJourneyClientConfig; update hasWellknownConfig to also validate that
config.serverConfig has a non-empty string baseUrl (i.e., check 'baseUrl' in
config.serverConfig && typeof config.serverConfig.baseUrl === 'string' &&
config.serverConfig.baseUrl.length > 0) so the guard reliably asserts
AsyncJourneyClientConfig.

In `@tools/user-scripts/package.json`:
- Around line 25-26: The package.json currently lists "vitest" under
dependencies; remove the duplicate "vitest" entry from the dependencies object
so vitest remains only in devDependencies (leave "@effect/vitest" as-is if
intended); update the dependencies block to delete the "vitest" key to ensure
test-only packages are not shipped in runtime dependencies.
🧹 Nitpick comments (2)
package.json (1)

52-52: Empty dependencies object is unnecessary.

The root package.json typically doesn't need a dependencies field for a monorepo. If this was added intentionally for a specific tooling requirement, consider adding a comment explaining its purpose; otherwise, it can be removed.

packages/oidc-client/src/lib/wellknown.api.ts (1)

12-38: Use the shared selector factory instead of recreating selectors.

The createWellknownSelector helper from @forgerock/sdk-oidc returns a memoized selector function, which can be directly called with state. Refactoring to use it aligns with the documented intent and avoids recreating the selector on each call.

♻️ Proposed refactor
-import { createSelector } from '@reduxjs/toolkit';
-
 import type { RootState } from './client.types.js';

 /**
  * Re-export the shared wellknown RTK Query API from `@forgerock/sdk-oidc`.
@@
 export { wellknownApi, createWellknownSelector } from '@forgerock/sdk-oidc';

 // Import locally for use in selector below
-import { wellknownApi } from '@forgerock/sdk-oidc';
+import { wellknownApi, createWellknownSelector } from '@forgerock/sdk-oidc';

@@
 export function wellknownSelector(wellknownUrl: string, state: RootState) {
-  const selector = createSelector(
-    wellknownApi.endpoints.configuration.select(wellknownUrl),
-    (result) => result?.data,
-  );
-  return selector(state);
+  return createWellknownSelector(wellknownUrl)(state);
 }

"cookie-parser": "^1.4.7",
"cors": "^2.8.5",
"express": "^4.21.2",
"express": "^5.2.1",
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat e2e/am-mock-api/package.json

Repository: ForgeRock/ping-javascript-sdk

Length of output: 484


🌐 Web query:

Express 5.0 TypeScript types support @types/express compatibility

💡 Result:

  • Express 5 does not ship its own TypeScript declarations (there’s no "types"/"typings" field in the express package metadata), so TypeScript support still comes from DefinitelyTyped (@types/*). [1]
  • Compatibility rule: match the major versions:
    • express@5 → install @types/express@5
    • express@4 → install/pin @types/express@4 (don’t mix with @types/express@5). [3]
  • This matters because projects on Express 4 have hit type-breakage when the resolver pulls in @types/express@5 (incompatible). [4]
  • The @types/express package is actively published in the 5.x line for Express 5. [2]

Practical install/pin examples

npm i express@5
npm i -D `@types/express`@^5
npm i express@^4
npm i -D `@types/express`@^4

Sources: [1] [2] [3] [4]


🌐 Web query:

body-parser 2.2.2 Express 5.x compatibility

💡 Result:

body-parser@2.2.2 is compatible with Express 5.x.

  • No Express peer-dependency: body-parser is framework-agnostic middleware that you mount with app.use(bodyParser.json()), app.use(bodyParser.urlencoded(...)), etc. (it just populates req.body). [1]
  • Node requirement aligns with Express 5: body-parser@2.2.2 requires Node >= 18, which matches Express 5’s Node baseline. [2] [3]
  • In Express 5, prefer built-ins when possible: Express 5 expects you to explicitly enable parsing; req.body is undefined unless you use a parser (e.g., express.json() / express.urlencoded()). [4]
  • Security note: 2.2.0 had a DoS advisory; upgrading to >= 2.2.1 (including 2.2.2) is the fix. [5]

Recommended (Express 5):

app.use(express.json());
app.use(express.urlencoded({ extended: false }));

(You only need body-parser directly if you specifically want its APIs/options beyond Express’s built-ins.) [4]

Sources: [1] [2] [3] [4] [5]


🌐 Web query:

cookie-parser 1.4.7 Express 5.x compatibility

💡 Result:

cookie-parser@1.4.7 is compatible with Express 5.x in practice:

  • It has no Express peer dependency and is just standard Connect/Express middleware that reads the Cookie header and populates req.cookies / req.signedCookies. [1]
  • Express 5.x docs still describe req.cookies as being provided when using cookie-parser middleware, indicating the integration model is unchanged. [2]
  • Express 5 requires Node.js 18+, while cookie-parser@1.4.7’s engines is node >= 0.8.0, so there’s no Node version conflict. [1], [2]

Typical usage remains the same:

import express from "express";
import cookieParser from "cookie-parser";

const app = express();
app.use(cookieParser(process.env.COOKIE_SECRET)); // optional secret

Sources: [1] (cookie-parser 1.4.7 package.json on UNPKG), [2] (Express 5.x API docs req.cookies).


Update @types/express to v5.x for Express 5 compatibility.

Express has been upgraded from v4 to v5, a major version with breaking changes. The type definitions (@types/express) remain at ^4.17.17, which only provides types for Express 4.x. Express 5.x does not ship built-in TypeScript declarations and relies on @types/express@^5 from DefinitelyTyped.

Update @types/express in devDependencies to ^5.0.0 (or later) to match the Express 5.x major version and avoid type errors and incorrect type hints.

🤖 Prompt for AI Agents
In `@e2e/am-mock-api/package.json` at line 14, Update the devDependency entry for
"@types/express" to a v5-compatible range (e.g., "^5.0.0") so it matches the
installed "express" v5.x and avoids type mismatches; modify the "@types/express"
version in package.json (devDependencies) and then reinstall/update the lockfile
(npm/yarn) to ensure the new types are applied.

Comment on lines +33 to +34
"vite": "catalog:vite",
"vitest-canvas-mock": "catalog:vitest"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Move vite and vitest-canvas-mock to devDependencies only.

These are build/test tools and should not be listed under dependencies. They are already correctly present in devDependencies (lines 38, 40). Having them in both sections is redundant and could cause issues with production builds.

Proposed fix
     "@forgerock/storage": "workspace:*",
     "@reduxjs/toolkit": "catalog:",
-    "tslib": "^2.3.0",
-    "vite": "catalog:vite",
-    "vitest-canvas-mock": "catalog:vitest"
+    "tslib": "^2.3.0"
   },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"vite": "catalog:vite",
"vitest-canvas-mock": "catalog:vitest"
"@forgerock/storage": "workspace:*",
"@reduxjs/toolkit": "catalog:",
"tslib": "^2.3.0"
},
🤖 Prompt for AI Agents
In `@packages/journey-client/package.json` around lines 33 - 34, Remove the
build/test tools from the runtime dependencies: delete the "vite" and
"vitest-canvas-mock" entries from the dependencies block so they exist only
under devDependencies; locate the package.json dependencies array (look for the
"vite" and "vitest-canvas-mock" keys) and remove those keys, leaving the current
devDependencies entries intact.

Comment on lines +38 to +106
export function hasWellknownConfig(config: JourneyConfigInput): config is AsyncJourneyClientConfig {
return (
'serverConfig' in config &&
typeof config.serverConfig === 'object' &&
config.serverConfig !== null &&
'wellknown' in config.serverConfig &&
typeof config.serverConfig.wellknown === 'string' &&
config.serverConfig.wellknown.length > 0
);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Validate baseUrl in the type guard.

AsyncJourneyClientConfig requires serverConfig.baseUrl. As written, a config with only wellknown will be narrowed to the async type, which can lead to runtime errors later.

✅ Proposed fix
 export function hasWellknownConfig(config: JourneyConfigInput): config is AsyncJourneyClientConfig {
   return (
     'serverConfig' in config &&
     typeof config.serverConfig === 'object' &&
     config.serverConfig !== null &&
+    'baseUrl' in config.serverConfig &&
+    typeof config.serverConfig.baseUrl === 'string' &&
+    config.serverConfig.baseUrl.length > 0 &&
     'wellknown' in config.serverConfig &&
     typeof config.serverConfig.wellknown === 'string' &&
     config.serverConfig.wellknown.length > 0
   );
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export function hasWellknownConfig(config: JourneyConfigInput): config is AsyncJourneyClientConfig {
return (
'serverConfig' in config &&
typeof config.serverConfig === 'object' &&
config.serverConfig !== null &&
'wellknown' in config.serverConfig &&
typeof config.serverConfig.wellknown === 'string' &&
config.serverConfig.wellknown.length > 0
);
export function hasWellknownConfig(config: JourneyConfigInput): config is AsyncJourneyClientConfig {
return (
'serverConfig' in config &&
typeof config.serverConfig === 'object' &&
config.serverConfig !== null &&
'baseUrl' in config.serverConfig &&
typeof config.serverConfig.baseUrl === 'string' &&
config.serverConfig.baseUrl.length > 0 &&
'wellknown' in config.serverConfig &&
typeof config.serverConfig.wellknown === 'string' &&
config.serverConfig.wellknown.length > 0
);
}
🤖 Prompt for AI Agents
In `@packages/journey-client/src/lib/wellknown.utils.ts` around lines 38 - 46, The
type guard hasWellknownConfig currently only checks for serverConfig.wellknown
and can incorrectly narrow JourneyConfigInput to AsyncJourneyClientConfig;
update hasWellknownConfig to also validate that config.serverConfig has a
non-empty string baseUrl (i.e., check 'baseUrl' in config.serverConfig && typeof
config.serverConfig.baseUrl === 'string' && config.serverConfig.baseUrl.length >
0) so the guard reliably asserts AsyncJourneyClientConfig.

* @param value - The value to check
* @returns True if value is a non-null object
*/
function isObject(value: unknown): value is Record<string, unknown> {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't add a generic to this since the type guard would become unsafe.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@packages/journey-client/src/lib/config.types.ts`:
- Around line 11-30: The JourneyClientConfig docs require baseUrl but the type
inherits optional serverConfig from BaseConfig; update JourneyClientConfig to
override serverConfig as required with baseUrl enforced (e.g., replace the
inherited optional serverConfig with a non-optional field that ensures baseUrl:
string is present) so the type matches the documentation; alternatively, if
serverConfig may remain optional, update the doc comment to remove the
"required" wording — change either the JourneyClientConfig type (override
serverConfig) or the doc text to keep them consistent (refer to
JourneyClientConfig, BaseConfig, and serverConfig/baseUrl in your change).
🧹 Nitpick comments (7)
e2e/davinci-app/package.json (1)

19-19: Optional: drop empty devDependencies block.
Keeps the manifest leaner if no dev-only deps are required.

♻️ Proposed cleanup
-  "devDependencies": {},
package.json (1)

52-52: Optional: remove empty dependencies.
Avoids a redundant block if not needed.

♻️ Proposed cleanup
-  "dependencies": {},
packages/journey-client/src/lib/device/device-profile.test.ts (1)

10-10: Type update from SpyInstance to Mock is correct.

The change aligns with Vitest's API evolution where SpyInstance was deprecated in favor of Mock. However, the explicit callable signatures in the generic parameter are verbose and may not be necessary.

Consider simplifying to:

let warnSpy: Mock<typeof console.warn>;

This achieves the same type safety with less boilerplate.

Also applies to: 89-92

packages/sdk-utilities/src/lib/wellknown/wellknown.utils.test.ts (1)

12-72: Test coverage is good; consider simplifying nested describe blocks.

The test cases cover the main validation scenarios well. However, the nested describe blocks are redundant—each wraps only a single it() and duplicates the test description.

♻️ Suggested simplification
 describe('isValidWellknownUrl', () => {
-  describe('isValidWellknownUrl_HttpsUrl_ReturnsTrue', () => {
-    it('should return true for HTTPS URL', () => {
-      expect(isValidWellknownUrl('https://am.example.com/.well-known/openid-configuration')).toBe(
-        true,
-      );
-    });
-  });
+  it('should return true for HTTPS URL', () => {
+    expect(isValidWellknownUrl('https://am.example.com/.well-known/openid-configuration')).toBe(
+      true,
+    );
+  });
   // ... similarly for other test cases

Additionally, consider adding edge cases:

  • URLs without ports (e.g., https://am.example.com/.well-known/openid-configuration)
  • URLs with query strings or fragments
  • Null/undefined inputs (if the function signature allows)
packages/journey-client/src/lib/config.types.ts (1)

32-50: Reduce duplication by extending PathsConfig.
WellknownServerConfig mirrors PathsConfig fields. Extending PathsConfig keeps future additions consistent.

♻️ Proposed refactor
-export interface WellknownServerConfig {
-  /** Base URL for AM-specific endpoints (authenticate, sessions) */
-  baseUrl: string;
-  /** URL to the OIDC well-known configuration endpoint */
-  wellknown: string;
-  /** Custom path overrides for endpoints */
-  paths?: PathsConfig['paths'];
-  /** Request timeout in milliseconds */
-  timeout?: number;
-}
+export interface WellknownServerConfig extends PathsConfig {
+  /** URL to the OIDC well-known configuration endpoint */
+  wellknown: string;
+}
packages/journey-client/src/lib/journey.api.ts (1)

118-123: Consider extracting config retrieval into a helper function.

The same config retrieval and validation pattern is repeated in start, next, and terminate endpoints (lines 118-123, 152-157, 183-188). A helper could reduce duplication:

♻️ Optional refactor
function getConfigFromState(api: BaseQueryApi): { realmPath?: string; serverConfig: ServerConfig } {
  const state = api.getState() as JourneyRootState;
  const config = state.journey.config;
  if (!config?.serverConfig) {
    throw new Error('Server configuration is missing.');
  }
  return { realmPath: config.realmPath, serverConfig: config.serverConfig };
}

Then in each endpoint:

-const state = api.getState() as JourneyRootState;
-const config = state.journey.config;
-if (!config?.serverConfig) {
-  throw new Error('Server configuration is missing.');
-}
-const { realmPath, serverConfig } = config;
+const { realmPath, serverConfig } = getConfigFromState(api);
packages/journey-client/src/lib/client.store.ts (1)

131-135: Structured error information is discarded.

createWellknownError returns a GenericError with error, message, type, and status fields, but only message is used when throwing. Consider preserving the structured error or using a custom error class:

♻️ Preserve structured error information

Option 1 - Create a custom error class:

class WellknownError extends Error {
  constructor(public readonly genericError: GenericError) {
    super(genericError.message);
    this.name = 'WellknownError';
  }
}
// Usage:
throw new WellknownError(genericError);

Option 2 - Add cause to the error:

-throw new Error(genericError.message);
+throw new Error(genericError.message, { cause: genericError });

Comment on lines +11 to 30
/**
* Standard journey client configuration with explicit baseUrl.
*
* Use this when you want to configure the AM server directly without
* OIDC well-known endpoint discovery.
*
* @example
* ```typescript
* const config: JourneyClientConfig = {
* serverConfig: {
* baseUrl: 'https://am.example.com/am/',
* },
* realmPath: 'alpha',
* };
* ```
*/
export interface JourneyClientConfig extends BaseConfig {
middleware?: Array<RequestMiddleware>;
realmPath?: string;
// Add any journey-specific config options here
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Align JourneyClientConfig typing with the “baseUrl required” doc.
Docs state baseUrl is required, but BaseConfig keeps serverConfig optional. If the intent is to require baseUrl for this config shape, consider overriding serverConfig to be required (or soften the doc if optional is still valid).

🔧 Suggested type tightening (if requirement is intended)
 export interface JourneyClientConfig extends BaseConfig {
+  serverConfig: PathsConfig;
   middleware?: Array<RequestMiddleware>;
   realmPath?: string;
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/**
* Standard journey client configuration with explicit baseUrl.
*
* Use this when you want to configure the AM server directly without
* OIDC well-known endpoint discovery.
*
* @example
* ```typescript
* const config: JourneyClientConfig = {
* serverConfig: {
* baseUrl: 'https://am.example.com/am/',
* },
* realmPath: 'alpha',
* };
* ```
*/
export interface JourneyClientConfig extends BaseConfig {
middleware?: Array<RequestMiddleware>;
realmPath?: string;
// Add any journey-specific config options here
}
/**
* Standard journey client configuration with explicit baseUrl.
*
* Use this when you want to configure the AM server directly without
* OIDC well-known endpoint discovery.
*
* `@example`
*
🤖 Prompt for AI Agents
In `@packages/journey-client/src/lib/config.types.ts` around lines 11 - 30, The
JourneyClientConfig docs require baseUrl but the type inherits optional
serverConfig from BaseConfig; update JourneyClientConfig to override
serverConfig as required with baseUrl enforced (e.g., replace the inherited
optional serverConfig with a non-optional field that ensures baseUrl: string is
present) so the type matches the documentation; alternatively, if serverConfig
may remain optional, update the doc comment to remove the "required" wording —
change either the JourneyClientConfig type (override serverConfig) or the doc
text to keep them consistent (refer to JourneyClientConfig, BaseConfig, and
serverConfig/baseUrl in your change).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants